﻿using MQTTnet;
using MQTTnet.Client.Publishing;
using MQTTnet.Diagnostics.Logger;
using MQTTnet.Formatter;
using MQTTnet.Protocol;
using MQTTnet.Server;
using System;
using System.Collections.Generic;
using System.Net;
using System.Security.Authentication;
using System.Threading.Tasks;

namespace MqttLib
{
    public class MqttServerUtil : IMqttNetBase
    {

        private bool disposedValue;
        private IMqttServer _server;
        private IMqttServerOptions _options;

        #region 接口属性实现
        public string Username { get; private set; }
        public string Pwd { get; private set; }
        public string IP { get; private set; }
        public int Port { get; private set; }
        public uint MaxPacketSize { get; private set; }
        public MqttProtocolVersion MqttVersion { get; private set; }
        public SslProtocols SslVersion { get; private set; }
        public TimeSpan CommunicationTimeout { get; private set; }
        #endregion

        public SortedSet<string> ClientIds { get; private set; }

        public Action<MqttApplicationMessageReceivedEventArgs> ServerMsgReceivedAction { get; set; }

        public MqttServerUtil(
            string ip,
            int port,
            string username,
            string password,
            uint communicationTimeout = 36000,
            SslProtocols sslProtocols = SslProtocols.None
            )
        {
            ClientIds = new SortedSet<string>();
            IP = ip;
            Port = port;
            Username = username;
            Pwd = password;
            CommunicationTimeout = TimeSpan.FromSeconds(communicationTimeout);
            SslVersion = sslProtocols;
        }

        private void Init()
        {
            IMqttNetLogger logger = new MqttNetEventLogger();

            MqttServerOptionsBuilder serverOptionsBuilder = new MqttServerOptionsBuilder()
                .WithDefaultEndpointBoundIPAddress(IPAddress.Parse(IP))
                .WithDefaultEndpointPort(Port)
                .WithDefaultCommunicationTimeout(CommunicationTimeout)
                .WithEncryptionSslProtocol(SslVersion);
            // 身份验证
            serverOptionsBuilder.WithConnectionValidator(valid =>
            {
                if (string.IsNullOrEmpty(valid.Username))
                {
                    valid.ReasonCode = MqttConnectReasonCode.Success;
                    return;
                }
                bool authentication = string.Equals(Username, valid.Username) && string.Equals(Pwd, valid.Password);
                valid.ReasonCode = authentication ? MqttConnectReasonCode.Success : MqttConnectReasonCode.BadUserNameOrPassword;
            });
            _options = serverOptionsBuilder.Build();

            _server = IMqttNetBase.Factory.CreateMqttServer(logger);
            _server.UseApplicationMessageReceivedHandler(ServerMsgReceivedAction);
            _server.ClientConnectedHandler = new MqttServerClientConnectedHandlerDelegate(ClientConnected);
            _server.ClientDisconnectedHandler = new MqttServerClientDisconnectedHandlerDelegate(ClientDisconnected);

        }

        public async Task<bool> StartAsync()
        {
            if (_server == null)
                Init();
            await _server.StartAsync(_options);
            return _server.IsStarted;
        }

        public async Task StopAsync()
        {
            await _server.StopAsync();
        }

        /// <summary>
        /// 客户端上线事件
        /// </summary>
        /// <param name="args"></param>
        private void ClientConnected(MqttServerClientConnectedEventArgs args)
        {
            ClientIds.Add(args.ClientId);
            Console.WriteLine($"ClientID:{args.ClientId} has connected success!");
        }

        /// <summary>
        /// 客户端离线事件
        /// </summary>
        /// <param name="args"></param>
        private void ClientDisconnected(MqttServerClientDisconnectedEventArgs args)
        {
            ClientIds.RemoveWhere(s => s.Equals(args.ClientId));
            Console.WriteLine($"ClientID:{args.ClientId} has disconnected!");
        }

        public async Task<MqttClientPublishResult> Publish(MqttApplicationMessage msg)
        {
            if (!_server.IsStarted)
            {
                await _server.StartAsync(_options);
            }
            if (_server.IsStarted)
            {
                return await _server.PublishAsync(msg);
            }
            return new MqttClientPublishResult
            {
                ReasonCode = MqttClientPublishReasonCode.UnspecifiedError,
                ReasonString = "mqtt server尝试重新启动失败!"
            };
        }

        protected void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    // TODO: 释放托管状态(托管对象)
                    try { StopAsync().Wait(); } catch { }
                    _server.Dispose();
                }
                // TODO: 释放未托管的资源(未托管的对象)并重写终结器
                // TODO: 将大型字段设置为 null
                disposedValue = true;
            }
        }

        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

    }
}